"""
python auto_start.py
"""
import schedule
from tools.remove_temp_file import remove_temp_file
import time
import os
import json
import requests
import subprocess
from bdtime import tt


title = 'p2232_test'

temp_dir_name = 'tempdir/version'
os.makedirs(temp_dir_name, exist_ok=True)


def create_file_if_not_exist(file_name):
    if not os.path.exists(file_name):
        with open(file_name, 'w') as f:
            f.write('')
        return False
    return True


version_file_path = os.path.join(temp_dir_name, 'version.py')
create_file_if_not_exist(version_file_path)


def get_current_version():
    global version_file_path
    with open(version_file_path, 'r') as f:
        data = f.read()
    return eval(data) if data else 0


def save_current_version(current_version):
    global version_file_path
    with open(version_file_path, 'w') as f:
        f.write(f'{current_version}')


ENCODING = 'utf-8'
log_file_path = os.path.join(temp_dir_name, '.log')
create_file_if_not_exist(log_file_path)


def get_log_info():
    global log_file_path
    with open(log_file_path, 'r+', encoding=ENCODING) as f:
        ret = f.read()
    return ret


def save_log_info(log_info, override=False):
    global log_file_path

    if isinstance(log_info, bytes):
        log_info = log_info.decode(ENCODING)

    assert isinstance(log_info, str), 'log_info必须为str类型!'

    if override:
        model = 'w+'
    else:
        model = 'a+'

    if log_info and not log_info.endswith('\n'):
        log_info += '\n'

    with open(log_file_path, model, encoding=ENCODING) as f:
        f.write(f'{log_info}')


def unzip_to(src, dst):
    import shutil
    import zipfile

    TEMPDIR = 'tempdir'

    # src = obj.file.path
    # dst = "/server/media/tt.txt"
    # dst = obj.replace_path

    # assert len(dst) > 5 and len(dst.split('/')) > 3, f"别乱搞哦! 检测到[replace_path: {dst}]有安全性问题!"
    # os.path.exists(src)
    # zipfile.is_zipfile('/opt/server/tempdir/123.zip')
    # zipfile.is_zipfile('/opt/server/tempdir/dist.zip')
    if zipfile.is_zipfile(src):
        assert dst.endswith('/'), "目标路径若是文件夹, 填写的时候必须以\/结尾!"
        dir_name = os.path.basename(os.path.dirname(dst))
        os.makedirs(dst, exist_ok=True)

        zf = zipfile.ZipFile(src, 'r')

        zip_dir_root_name = "temp_zip_dir"
        temp_zip_root_path = os.path.join(TEMPDIR, zip_dir_root_name)
        os.makedirs(temp_zip_root_path, exist_ok=True)

        temp_file = f"zip_extract__{tt.time().__str__().replace('.', '')}"
        temp_path = os.path.join(temp_zip_root_path, temp_file)

        for file in zf.namelist():
            zf.extract(file, temp_path)

        # --- 将 temp_path 拷贝至 dst
        if os.path.exists(dst):
            shutil.rmtree(dst)

        unzip_dirpath = os.path.join(temp_path, dir_name)
        shutil.copytree(unzip_dirpath, dst)
        remove_temp_file(temp_zip_root_path)


current_version = get_current_version()
last_fetch_success = True        # 判断最近一次的远程版本检测是否成功
fetch_interval = 20  # 版本检测失败时的休眠间隔
job_interval = 10  # 工作间隔

next_start_job_time = None


first_run = True  # 是否第一次运行的flag


def download_file(download_url, temp_filename, temp_dirpath):
    response = requests.get(download_url)
    temp_filepath = os.path.join(temp_dirpath, temp_filename)
    os.makedirs(temp_dirpath, exist_ok=True)
    with open(os.path.realpath(temp_filepath), "wb") as f:
        f.write(response.content)
    return os.path.realpath(temp_filepath)


def start_manage(run_in_backend=False):
    assert os.path.exists('force_start.sh'), '未找到`force_start.sh`脚本!'

    cmd = 'bash force_start.sh'
    if run_in_backend:
        subprocess.Popen(cmd, shell=True)  # 用新的替换旧的
    else:
        os.system(cmd)


# 定义你要周期运行的函数
def job():
    import zipfile

    global current_version, first_run, last_fetch_success, fetch_interval, next_start_job_time, job_interval

    if next_start_job_time is not None:
        start_job_interval = next_start_job_time - tt.now(0)
        if start_job_interval > 0:
            if tt.now(0) % 10 == 0 and start_job_interval > 10:
                print(f'--- start_job_interval: 还剩{start_job_interval}秒开始下次job...')
            return

    print('*** start_job!')

    try:
        base_url = 'http://www.bodexiong.vip:2204'
        # url = f'{base_url}/api/user/UploadFile'
        url = f'{base_url}/api/comment/VersionController/'
        params = {
            'title': title
        }
        response_0 = requests.get(url, params)
        # print(response_0.content)
        if response_0.status_code != 200:
            msg = f'\n~~~ {tt.get_current_beijing_time_str(decimal_places=0)}\n--- http返回码错误? auto_updater将休眠{round(fetch_interval / 60, 2)}分钟...'
            if last_fetch_success:
                print(msg)
                tt.sleep(2)
                start_manage(run_in_backend=True)
                first_run = False
                # tt.sleep(fetch_interval)
            last_fetch_success = False
            next_start_job_time = tt.now(0) + fetch_interval
            raise ConnectionError(msg)
        else:
            next_start_job_time = None
            last_fetch_success = True

        response_dc: dict = json.loads(response_0.content)
        result = response_dc.get('result')
        page_dc = result.get('page_dc')
        assert page_dc.get('count_items'), 'count_items不能为0!'
        data = result.get('data')
        dc = data[-1]
        last_version = dc.get('last_version')
        print('--- auto_updater_check_time: ', tt.get_current_beijing_time_str(decimal_places=0), f'--- current_version: {current_version} -> last_version: {last_version}')

        check_interval = dc.get('check_interval', 10)
        if check_interval:
            # if check_interval != job_interval:
            #     job_interval = check_interval
            next_start_job_time = tt.now(0) + check_interval

        if current_version < last_version:
            print(f'--- 开始更新! ---')
            tt.sleep(2)

            _file = dc.get('file')
            if _file:
                file = f"{base_url}{dc.get('file')}"
            else:
                file = None

            # dst = '/opt/client/h5/dist/'
            dst = dc.get('f_dst', "")

            if dst and dst.strip().startswith('#'):
                dst = ""

            if dst and file:
                print(f'*** {tt.get_current_beijing_time_str()} --- 下载前端中...')
                temp_filepath = download_file(file, os.path.basename(file), 'tempdir')

                src = temp_filepath

                if zipfile.is_zipfile(src):
                    # assert zipfile.is_zipfile(src), f'目标文件[{src}]必须为zip类型!'
                    print(f'*** {tt.get_current_beijing_time_str()} --- 前端下载完毕, 准备解压... {src}   ---   is_zipfile():', zipfile.is_zipfile(src))
                    unzip_to(src, dst)
                else:
                    _dir_path = os.path.dirname(dst)
                    os.makedirs(_dir_path, exist_ok=True)
                    import shutil
                    shutil.copy2(src, dst)
                    # if not os.path.exists(_dir_path):
                    #     1

            commands = dc.get('command')

            if commands and commands.strip():
                # 清理旧的日志
                save_log_info(
                    f'[ {current_version} -> {last_version} ]\n=== {tt.get_current_beijing_time_str(decimal_places=0)} ===\n\n--- output:\n',
                    override=True)
                # get_log_info()

                commands: str
                _commands = commands.strip()
                if _commands.startswith('#!/usr/bin/env bash'):
                    print('_commands')
                    _commands = _commands.replace('\r\n', '\n')
                    _commands = _commands.replace('\r', '')

                    os.makedirs('tempdir/shells', exist_ok=True)
                    commands_file_path = 'tempdir/shells/commands.sh'
                    with open(commands_file_path, 'w+', encoding=ENCODING) as f:
                        f.write(_commands)
                    command_i = f'bash {commands_file_path}'
                    # os.system(command_i)
                    p = subprocess.Popen(command_i, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                    output, error = p.communicate()
                    _output = output.decode(ENCODING)
                    if error:
                        msg = f"\n>>> {command_i}\n*** error:\n {error.decode(ENCODING)}\n"
                        if _output:
                            msg += f"*** output: {_output}\n"
                        save_log_info(msg)
                    else:
                        msg = f'>>> {command_i}\n{_output}'
                        save_log_info(msg)
                else:
                    print('command_ls')

                    command_ls = commands.split('\n')

                    for command_i in command_ls:
                        # command_i: str
                        command_i = command_i.strip()
                        if not command_i:
                            continue

                        p = subprocess.Popen(command_i, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                        output, error = p.communicate()
                        _output = output.decode(ENCODING)
                        if error:
                            msg = f"\n>>> {command_i}\n*** error:\n {error.decode(ENCODING)}\n"
                            if _output:
                                msg += f"*** output: {_output}\n"
                            save_log_info(msg)
                        else:
                            msg = f'>>> {command_i}\n{_output}'
                            save_log_info(msg)

                log_info = get_log_info()

                _id = dc.get('id')
                put_url = f'{url}{_id}/'
                payload = {
                    'log_info': log_info
                }
                response_put = requests.put(put_url, data=payload)
                assert response_put.status_code == 200, 'put返回码错误!'

                put_content_dc = json.loads(response_put.content)

                assert int(put_content_dc.get('status')) == 200, 'put_content_dc的status错误!'

                print('--- commands运行结束.')

            is_need_migrate = dc.get('is_need_migrate')
            if is_need_migrate:
                # 如果需要迁移, 则 虚假迁移 -> 拉取新代码 -> 正式迁移
                _migrate_apps = dc.get('migrate_apps', "")
                migrate_apps = [i for i in _migrate_apps.split(';') if i != '']
                # migrate_apps = ['index', '__all__']
                # migrate_apps = ['index', 'information']
                if '__all__' in migrate_apps:
                    index_all = migrate_apps.index('__all__')
                    migrate_apps[index_all] = ''

                for migrate_app_i in migrate_apps:
                    print(f'*** will migrate: [{migrate_app_i}]')
                    os.system(f'bash shells/fake_migrate_db.sh {migrate_app_i}')

                tt.sleep(1)
                os.system('bash shells/forcepull.sh master')

                for migrate_app_i in migrate_apps:
                    os.system(f'bash shells/migrate_db.sh {migrate_app_i}')

                tt.sleep(1)

            is_restart = dc.get('is_restart')
            if is_restart:
                start_manage(run_in_backend=True)

            current_version = last_version
            save_current_version(current_version)
        else:
            if first_run:
                print('========= first_run!')
                tt.sleep(2)
                start_manage(run_in_backend=True)
                first_run = False

    except Exception as e:
        print('******** Error: ', e)
        next_start_job_time = tt.now(0) + fetch_interval
        first_run = False


schedule.every(1).seconds.do(job)
# schedule.every(5).minutes.do(job)               # 每隔 10 分钟运行一次 job 函数
# schedule.every().hour.do(job)                    # 每隔 1 小时运行一次 job 函数
# schedule.every().day.at("10:30").do(job)         # 每天在 10:30 时间点运行 job 函数
# schedule.every().monday.do(job)                  # 每周一 运行一次 job 函数
# schedule.every().wednesday.at("13:15").do(job)   # 每周三 13：15 时间点运行 job 函数
# schedule.every().minute.at(":17").do(job)        # 每分钟的 17 秒时间点运行 job 函数


if __name__ == '__main__':
    flag = os.path.exists('.flag')
    # flag = 0
    if flag:
        start_manage()
    else:
        job()
        while True:
            schedule.run_pending()   # 运行所有可以运行的任务
            time.sleep(1)

